use actix_web::{delete, get, HttpRequest, HttpResponse, post, web};
use serde::{Deserialize, Serialize};
use surrealdb::sql::Thing;
use crate::common::actix_utils::web_param;
use crate::common::global_config::AppState;

pub fn router_config(cfg: &mut web::ServiceConfig) {
    cfg.service(user_list);
    cfg.service(user_save);
    cfg.service(find_param);
    cfg.service(delete_user);
}

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: Option<Thing>,
    username: String,
    password: String,
    address: Option<String>,
}

#[get("/user")]
pub async fn user_list(state: web::Data<AppState>) -> HttpResponse {
    let db = &state.db;
    let mut r = db.query("select * from t_user").await.unwrap();
    let result: Vec<User> = r.take(0).unwrap();
    HttpResponse::Ok().json(result)
}

#[post("/user")]
pub async fn user_save(state: web::Data<AppState>, data: web::Json<User>) -> HttpResponse {
    log::debug!("user_save data:{data:?}");
    let db = &state.db;
    let r = if data.id.is_none() {
        let mut vec = db.create::<Vec<User>>("t_user").content(data).await.unwrap();
        if vec.len() > 0 { Some(vec.remove(0)) } else { None }
    } else {
        let id = data.id.clone().unwrap().id.to_raw();
        log::debug!("user_save id:{id}");
        db.update::<Option<User>>(("t_user", id)).content(data).await.unwrap()
    };
    HttpResponse::Ok().json(r)
}


#[get("/user/find_param")]
pub async fn find_param(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
    let db = &state.db;
    let param = web_param(&req);
    log::debug!("find_param param:{param:?}");

    let mut sql = "select * from t_user where 1=1".to_owned();
    if let Some(v) = param.get_str("username") {
        sql += format!(" and username = '{}'", v).as_str();
    }
    if let Some(v) = param.get_str("address_like") {
        sql += format!(" and address contains '{}'", v).as_str();
    }

    log::debug!("find_param sql:{sql}");

    let mut r = db.query(sql).await.unwrap();
    let r: Vec<User> = r.take(0).unwrap();

    HttpResponse::Ok().json(r)
}

#[delete("/user/{id}")]
pub async fn delete_user(state: web::Data<AppState>, path: web::Path<String>) -> HttpResponse {
    log::debug!("准备删除");
    let db = &state.db;
    let id = path.into_inner();
    log::debug!("要删除的id:{id}");
    let r: Option<User> = db.delete(("t_user", id)).await.unwrap();
    HttpResponse::Ok().json(r)
}

#[cfg(test)]
mod tests {
    use actix_web::{test};
    use serde_json::json;
    use urlencoding::encode;

    use crate::common::test_utils::{init_logger, init_test_app, init_test_app_with_db, init_test_db, resolve_response};

    use super::*;

    #[test]
    async fn test_user_list() {
        init_logger();
        let app = init_test_app(|cfg| {
            router_config(cfg);
        }).await;
        let req = test::TestRequest::get().uri("/user").to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        assert_eq!("[]", body);
    }

    #[test]
    async fn test_user_save() {
        init_logger();
        let app = init_test_app(|cfg| {
            router_config(cfg);
        }).await;
        let req = test::TestRequest::post().uri("/user").set_json(json!({
            "username": "123",
            "password": "333"
        })).to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        let mut user: User = serde_json::from_str(&body).unwrap();
        log::debug!("user:{user:?}");
        user.username = "222".to_owned();

        let req = test::TestRequest::post().uri("/user").set_json(&user).to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        log::debug!("body:{body}");
        let user1: User = serde_json::from_str(&body).unwrap();
        assert_eq!(user.id, user1.id);
        assert_eq!("222", &user1.username);
    }

    #[test]
    async fn test_find_user() {
        init_logger();
        let db = init_test_db().await;
        let app = init_test_app_with_db(db.clone(), |cfg| {
            cfg.service(user_save);
            cfg.service(find_param);
        }).await;

        let req = test::TestRequest::post().uri("/user").set_json(json!({
            "username": "123",
            "password": "333",
            "address": "辽宁省大连市高新园区"
        })).to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        log::debug!("body:{body}");

        let url = format!("/user/find_param?address_like={}&username={}", encode("大连"), "123");
        log::debug!("url:{url}");
        let req = test::TestRequest::get().uri(&url).to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        log::debug!("body:{body}");
        let user: Vec<User> = serde_json::from_str(&body).unwrap();
        log::debug!("user:{user:?}");

        assert_eq!(1, user.len());
        let id = &user[0].id;
        assert!(id.is_some());
    }

    #[test]
    async fn test_delete_user() {
        init_logger();
        let app = init_test_app(|cfg| { router_config(cfg); }).await;

        let req = test::TestRequest::post().uri("/user").set_json(json!({
            "username": "123",
            "password": "333",
            "address": "辽宁省大连市高新园区"
        })).to_request();
        test::call_service(&app, req).await;

        let req = test::TestRequest::get().uri("/user").to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        let mut r: Vec<User> = serde_json::from_str(&body).unwrap();
        log::debug!("新增之后r:{r:?}");
        assert_eq!(1, r.len());

        let id = &r[0].id.take().unwrap().id.to_string();
        log::debug!("id:{id}");
        let url = format!("/user/{id}");
        log::debug!("删除url:{url}");
        let req = test::TestRequest::delete().uri(&url).to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        log::debug!("删除返回:{body}");

        let req = test::TestRequest::get().uri("/user").to_request();
        let res = test::call_service(&app, req).await;
        let body = resolve_response(res).await;
        let r: Vec<User> = serde_json::from_str(&body).unwrap();
        log::debug!("删除之后r:{r:?}");
        assert_eq!(0, r.len());
    }
}